﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace captcha_maker
{
    public partial class Form1 : Form
    {
        readonly static Size trainDataSize = new Size(20, 25);
        readonly static Random random = new Random();
        readonly static String[] mPossibleChars = new string[] { "A", "B", "C", "D", "E", "F", "G",
                                                                   "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"};

        private Bitmap captchaImg;

        private class CaptchaChar
        {
            public Bitmap BmpImage { get; private set; }
            public Bitmap SkelImage { get; private set; }

            public byte[] BoolRepresentation { get; private set; }

            private readonly string mChar;
            private readonly float mRot;
            private readonly Font mFont;
            private readonly Color mBackColor;

            public int Width { get; private set; }
            public int Height { get; private set; }

            public string Char() { return mChar;}


            public CaptchaChar(string _char, string _font, int _size, float _rot, Color _backColor)
            {
                mChar = _char;
                mRot = _rot;
                mBackColor = _backColor;

                mFont = new Font(_font, _size, FontStyle.Regular, GraphicsUnit.Pixel);

                CreateBitmap();

                BoolRepresentation = BmpUtils.GetBoolRepresentation(SkelImage);
            }

            public void CreateTrainData()
            {
                string guid = System.Guid.NewGuid().ToString();
                string filename = "TrainData\\" + mChar + "_" + guid + "_d.dat";

                using (FileStream stream = new FileStream(filename, FileMode.Create))
                {
                    using (BinaryWriter writer = new BinaryWriter(stream))
                    {
                        writer.Write(BoolRepresentation.ToArray());
                        writer.Close();
                    }
                }

                int[] results = new int[mPossibleChars.Length * 3];
                results[GetResultIndex()] = 1;

                filename = "TrainData\\" + mChar + "_" + guid + "_r.dat";
                using (FileStream stream = new FileStream(filename, FileMode.Create))
                {
                    using (BinaryWriter writer = new BinaryWriter(stream))
                    {
                        for (int i = 0; i < results.Length; i++)
                            writer.Write(Convert.ToBoolean(results[i]));

                        writer.Close();
                    }
                }
            }


            private int GetResultIndex()
            {
                int idx = GetCharIdx() + mPossibleChars.Length;

                if (mRot < -5)
                    idx -= mPossibleChars.Length;

                if (mRot > 5)
                    idx += mPossibleChars.Length;


                return idx;
            }

            private int GetCharIdx()
            {
                for (int i = 0; i < mPossibleChars.Length; i++)
                {
                    if (mChar == mPossibleChars[i])
                        return i;
                }

                throw new NotImplementedException();
            }

            private void CreateBitmap()
            {
                BmpImage = new Bitmap(1, 1);

                // Create a graphics object to measure the text's width and height.
                Graphics objGraphics = Graphics.FromImage(BmpImage);

                // This is where the bitmap size is determined.
                Width = (int)objGraphics.MeasureString(mChar, mFont).Width + 15;
                Height = (int)objGraphics.MeasureString(mChar, mFont).Height + 10;
                objGraphics.Dispose();

                // Create the bmpImage again with the correct size for the text and font.
                BmpImage = new Bitmap(BmpImage, new Size(Width, Height));

                // Add the colors to the new bitmap.
                objGraphics = Graphics.FromImage(BmpImage);
                objGraphics.RotateTransform(mRot);

                // Set Background color
                objGraphics.Clear(mBackColor);
                objGraphics.SmoothingMode = SmoothingMode.AntiAlias;
                objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;

                // Get a color != from the background color
                int r, g, b;
                do
                {
                    r = random.Next(255);
                    g = random.Next(255);
                    b = random.Next(255);
                } while (Color.FromArgb(r, g, b).ToArgb() == mBackColor.ToArgb());

                objGraphics.DrawString(mChar, mFont, new SolidBrush(Color.FromArgb(r, g, b)), 7, 5);
                objGraphics.Flush();
                objGraphics.Dispose();

                SkelImage = BmpUtils.ClearColors(BmpImage, mBackColor);
                SkelImage = BmpUtils.GetSkeleton(SkelImage);
                SkelImage = BmpUtils.ClipChar(SkelImage, trainDataSize);

                // A segunda passagem pelo algoritmo de eskelatização serve para remover
                // o ruído introduzido pelo algoritmo de redimensionamento da imagem
                SkelImage = BmpUtils.AddBorder(SkelImage,1);
                SkelImage = BmpUtils.GetSkeleton(SkelImage);
                SkelImage = BmpUtils.RemoveBorder(SkelImage, 1);

            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        List<CaptchaChar> chars;
        private void button1_Click(object sender, EventArgs e)
        {

            Color backColor = Color.FromArgb(random.Next(255), random.Next(255), random.Next(255));

            if(chars != null)
                chars.Clear();

            if (checkBox1.Checked)
            {

                chars = new List<CaptchaChar>
                                          {
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor),
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor), 
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor),
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor)
                                          };
            }
            else
            {
                chars = new List<CaptchaChar>
                                          {
                                              new CaptchaChar(txtChar1.Text,font1.SelectedItem.ToString(), Convert.ToInt32(size1.Value), Convert.ToInt32(rot1.Value),backColor),
                                              new CaptchaChar(txtChar2.Text,font2.SelectedItem.ToString(), Convert.ToInt32(size2.Value), Convert.ToInt32(rot2.Value),backColor), 
                                              new CaptchaChar(txtChar3.Text,font3.SelectedItem.ToString(), Convert.ToInt32(size3.Value), Convert.ToInt32(rot3.Value),backColor),
                                              new CaptchaChar(txtChar4.Text,font4.SelectedItem.ToString(), Convert.ToInt32(size4.Value), Convert.ToInt32(rot4.Value),backColor)
                                          };
            }


            GlueCaptchaChars(chars, backColor);

            FillSkeletons(chars, Color.Black);


            for (int i = 0; i < chars.Count; i++)
            {
                double certain = 0;
                int idx = NNLib.Solve(chars[i].BoolRepresentation, ref certain);
                while (idx > mPossibleChars.Length - 1)
                {
                    idx -= mPossibleChars.Length;
                }

                switch (i)
                {
                    case 0:
                        txtCharRes1.Text = mPossibleChars[idx].ToString();
                        txtCharsCertain1.Text = certain.ToString();
                        break;
                    case 1:
                        txtCharRes2.Text = mPossibleChars[idx].ToString();
                        txtCharsCertain2.Text = certain.ToString();
                        break;
                    case 2:
                        txtCharRes3.Text = mPossibleChars[idx].ToString();
                        txtCharsCertain3.Text = certain.ToString();
                        break;
                    case 3:
                        txtCharRes4.Text = mPossibleChars[idx].ToString();
                        txtCharsCertain4.Text = certain.ToString();
                        break;
                }
            }
        }

        private void FillSkeletons(List<CaptchaChar> _chars, Color _backColor)
        {

            Graphics objGraphics = skel1.CreateGraphics();
            objGraphics.Clear(_backColor);
            objGraphics.DrawImage(_chars[0].SkelImage, 0, 0);
            objGraphics.Dispose();

            objGraphics = skel2.CreateGraphics();
            objGraphics.Clear(_backColor);
            objGraphics.DrawImage(_chars[1].SkelImage, 0, 0);
            objGraphics.Dispose();

            objGraphics = skel3.CreateGraphics();
            objGraphics.Clear(_backColor);
            objGraphics.DrawImage(_chars[2].SkelImage, 0, 0);
            objGraphics.Dispose();

            objGraphics = skel4.CreateGraphics();
            objGraphics.Clear(_backColor);
            objGraphics.DrawImage(_chars[3].SkelImage, 0, 0);
            objGraphics.Dispose();

        }
        private string GetRandomChar()
        {
            return mPossibleChars[random.Next(mPossibleChars.Length)];
        }

        private string GetRandomFont()
        {
            return font1.Items[random.Next(font1.Items.Count)].ToString();
        }

        private int GetRandomSize()
        {
            return random.Next(Convert.ToInt32(size1.Minimum), Convert.ToInt32(size1.Maximum + 1));
        }

        private int GetRandomRot()
        {
            return random.Next(Convert.ToInt32(rot1.Minimum), Convert.ToInt32(rot1.Maximum + 1));
        }

        private void GlueCaptchaChars(List<CaptchaChar> _chars, Color _backColor)
        {
            int curX = random.Next(0, 5);
            captchaImg = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics objGraphics = Graphics.FromImage(captchaImg);

            objGraphics.Clear(_backColor);

            foreach (var c in _chars)
            {
                int posY = random.Next(0, pictureBox1.Height - c.Height);

                objGraphics.DrawImage(c.BmpImage, curX, posY);
                curX += c.Width + random.Next(1, 3);
            }
            objGraphics.Dispose();

            pictureBox1.Image = captchaImg;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            font1.SelectedIndex = 0;
            font2.SelectedIndex = 0;
            font3.SelectedIndex = 0;
            font4.SelectedIndex = 0;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            pictureBox1.Image.Save(@"D:\ISEC\2ºANO\2ºSemestre\Conhecimento e Raciocinio\Projecto\Captchas\captcha.png", ImageFormat.Png);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void button3_Click(object sender, EventArgs e)
        {
            captchaImg = new Bitmap(@"D:\ISEC\2ºANO\2ºSemestre\Conhecimento e Raciocinio\Projecto\Captchas\captcha.png");
            List<Bitmap> chars = BmpUtils.GetCaptchaChars(captchaImg, trainDataSize.Width, trainDataSize.Height);
            pictureBox1.Image = captchaImg;


            Color backColor = Color.Black;
            Graphics objGraphics = skel1.CreateGraphics();
            objGraphics.Clear(backColor);
            objGraphics.DrawImage(chars[0], 0, 0);
            objGraphics.Dispose();

            objGraphics = skel2.CreateGraphics();
            objGraphics.Clear(backColor);
            objGraphics.DrawImage(chars[1], 0, 0);
            objGraphics.Dispose();

            objGraphics = skel3.CreateGraphics();
            objGraphics.Clear(backColor);
            objGraphics.DrawImage(chars[2], 0, 0);
            objGraphics.Dispose();

            objGraphics = skel4.CreateGraphics();
            objGraphics.Clear(backColor);
            objGraphics.DrawImage(chars[3], 0, 0);
            objGraphics.Dispose();

        }

        private void button4_Click(object sender, EventArgs e)
        {
            for (int c = 0; c < mPossibleChars.Length; c++)
            {
                for (int i = 0; i < 100; i++)
                {
                    new CaptchaChar(mPossibleChars[c], GetRandomFont(), GetRandomSize(), GetRandomRot(), Color.White).CreateTrainData();
                }
            }

            MessageBox.Show("terminado!");
        }

        private void button5_Click(object sender, EventArgs e)
        {

            Color backColor = Color.FromArgb(random.Next(255), random.Next(255), random.Next(255));
            List<CaptchaChar> chars;

            if (checkBox1.Checked)
            {

                chars = new List<CaptchaChar>
                                          {
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor),
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor), 
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor),
                                              new CaptchaChar(GetRandomChar(), GetRandomFont(), GetRandomSize(), GetRandomRot(), backColor)
                                          };
            }
            else
            {
                chars = new List<CaptchaChar>
                                          {
                                              new CaptchaChar(txtChar1.Text,font1.SelectedItem.ToString(), Convert.ToInt32(size1.Value), Convert.ToInt32(rot1.Value),backColor),
                                              new CaptchaChar(txtChar2.Text,font2.SelectedItem.ToString(), Convert.ToInt32(size2.Value), Convert.ToInt32(rot2.Value),backColor), 
                                              new CaptchaChar(txtChar3.Text,font3.SelectedItem.ToString(), Convert.ToInt32(size3.Value), Convert.ToInt32(rot3.Value),backColor),
                                              new CaptchaChar(txtChar4.Text,font4.SelectedItem.ToString(), Convert.ToInt32(size4.Value), Convert.ToInt32(rot4.Value),backColor)
                                          };
            }

            /*
            int idx = NNLib.Solve(chars[0].BoolRepresentation);

            while (idx > mPossibleChars.Length -1)
            {

                idx -= mPossibleChars.Length;
            }

            string result = mPossibleChars[idx];*/
        }
    }
}
